Skip to content

refactor(guard): extract validateIntegrityField to eliminate triplicated switch blocks#4944

Merged
lpcox merged 3 commits into
mainfrom
copilot/duplicate-code-reduction-wasm-payload
May 1, 2026
Merged

refactor(guard): extract validateIntegrityField to eliminate triplicated switch blocks#4944
lpcox merged 3 commits into
mainfrom
copilot/duplicate-code-reduction-wasm-payload

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 1, 2026

The integrity-level validation switch (none|unapproved|approved|merged) was inlined identically three times in buildStrictLabelAgentPayload, meaning any new integrity level required three synchronized edits.

Changes

  • internal/guard/wasm_validate.go (new): single validateIntegrityField(fieldName string, raw interface{}) error helper — handles both the string type assertion and the validity switch in one place
  • internal/guard/wasm_payload.go: replaces the three duplicate blocks for integrity, disapproval-integrity, and endorser-min-integrity with calls to the helper
// before — repeated verbatim three times
disInt, ok := disIntRaw.(string)
if !ok {
    return nil, fmt.Errorf("invalid disapproval-integrity value: ...")
}
switch strings.ToLower(strings.TrimSpace(disInt)) {
case "none", "unapproved", "approved", "merged":
default:
    return nil, fmt.Errorf("invalid disapproval-integrity value: ...")
}

// after
if err := validateIntegrityField("disapproval-integrity", disIntRaw); err != nil {
    return nil, err
}

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build3106413706/b513/launcher.test /tmp/go-build3106413706/b513/launcher.test -test.testlogfile=/tmp/go-build3106413706/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -o .cfg 3753103/b314/ x_amd64/vet -p github.com/githu--version -lang=go1.25 x_amd64/vet .cfg�� 3753103/b364/_pkg_.a ache/go/1.25.9/x64/src/net/http/internal/httpcommon/httpcommon.ggoogle.golang.org/protobuf/types-qE x_amd64/vet --gdwarf-5 --64 -o 42W3fCn/gP4cHa5aRI3EQQAZUMxk (dns block)
  • invalid-host-that-does-not-exist-12345.com
    • Triggering command: /tmp/go-build3106413706/b495/config.test /tmp/go-build3106413706/b495/config.test -test.testlogfile=/tmp/go-build3106413706/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build3106413706/b393/vet.cfg 1.80.0/resolver/dns/dns_resolver.go 64/src/crypto/x509/cert_pool.go x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet -o g_.a -trimpath x_amd64/vet -p gzip -lang=go1.25 x_amd64/vet (dns block)
  • nonexistent.local
    • Triggering command: /tmp/go-build3106413706/b513/launcher.test /tmp/go-build3106413706/b513/launcher.test -test.testlogfile=/tmp/go-build3106413706/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -o .cfg 3753103/b314/ x_amd64/vet -p github.com/githu--version -lang=go1.25 x_amd64/vet .cfg�� 3753103/b364/_pkg_.a ache/go/1.25.9/x64/src/net/http/internal/httpcommon/httpcommon.ggoogle.golang.org/protobuf/types-qE x_amd64/vet --gdwarf-5 --64 -o 42W3fCn/gP4cHa5aRI3EQQAZUMxk (dns block)
  • slow.example.com
    • Triggering command: /tmp/go-build3106413706/b513/launcher.test /tmp/go-build3106413706/b513/launcher.test -test.testlogfile=/tmp/go-build3106413706/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -o .cfg 3753103/b314/ x_amd64/vet -p github.com/githu--version -lang=go1.25 x_amd64/vet .cfg�� 3753103/b364/_pkg_.a ache/go/1.25.9/x64/src/net/http/internal/httpcommon/httpcommon.ggoogle.golang.org/protobuf/types-qE x_amd64/vet --gdwarf-5 --64 -o 42W3fCn/gP4cHa5aRI3EQQAZUMxk (dns block)
  • this-host-does-not-exist-12345.com
    • Triggering command: /tmp/go-build3106413706/b522/mcp.test /tmp/go-build3106413706/b522/mcp.test -test.testlogfile=/tmp/go-build3106413706/b522/testlog.txt -test.paniconexit0 -test.timeout=10m0s -I .cfg -I x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet .cfg�� 3753103/b400/_pkg_.a -dynimport x_amd64/vet -dynout g/grpc/internal//usr/bin/runc p/bin/git x_amd64/vet (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Refactor duplicate integrity-level switch validation in wasm_payload.go refactor(guard): extract validateIntegrityField to eliminate triplicated switch blocks May 1, 2026
Copilot finished work on behalf of lpcox May 1, 2026 14:58
Copilot AI requested a review from lpcox May 1, 2026 14:58
@lpcox lpcox marked this pull request as ready for review May 1, 2026 15:04
Copilot AI review requested due to automatic review settings May 1, 2026 15:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors WASM guard policy validation by centralizing integrity-level (none|unapproved|approved|merged) checks into a single helper, reducing duplicated validation logic in the label-agent payload builder.

Changes:

  • Added a new validateIntegrityField(fieldName, raw) helper to validate integrity-level fields.
  • Replaced three duplicated integrity validation blocks in buildStrictLabelAgentPayload with calls to the helper.
Show a summary per file
File Description
internal/guard/wasm_validate.go Introduces shared integrity-level validation helper used by multiple policy fields.
internal/guard/wasm_payload.go Uses the helper to validate integrity, disapproval-integrity, and endorser-min-integrity, removing duplicated switch blocks.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment thread internal/guard/wasm_payload.go Outdated
Comment on lines +117 to +118
if err := validateIntegrityField("integrity", integrityRaw); err != nil {
return nil, err
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The integrity validation error uses fieldName="integrity" even when the provided key was "min-integrity" (the preferred key). This can produce confusing errors for users supplying "min-integrity". Consider tracking which key was actually present (e.g., set a fieldName based on whether allowOnly["min-integrity"] or allowOnly["integrity"] was used) and pass that to validateIntegrityField so the error message matches the input field name.

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +20
// validateIntegrityField returns an error if raw is not a valid integrity-level
// string. fieldName is used in the error message (e.g. "disapproval-integrity").
func validateIntegrityField(fieldName string, raw interface{}) error {
s, ok := raw.(string)
if !ok {
return fmt.Errorf("invalid %s value: expected one of none|unapproved|approved|merged", fieldName)
}
switch strings.ToLower(strings.TrimSpace(s)) {
case "none", "unapproved", "approved", "merged":
return nil
default:
return fmt.Errorf("invalid %s value: expected one of none|unapproved|approved|merged", fieldName)
}
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateIntegrityField still duplicates the allowed-values string in multiple places (the error message is repeated in both the type-assertion failure and default case, and the allowed values are also duplicated in the switch). To fully achieve the goal of “single source of truth” for integrity levels, consider centralizing the allowed values (e.g., a const/slice + helper to format the error) so adding a new integrity level requires changing it in only one place.

Suggested change
// validateIntegrityField returns an error if raw is not a valid integrity-level
// string. fieldName is used in the error message (e.g. "disapproval-integrity").
func validateIntegrityField(fieldName string, raw interface{}) error {
s, ok := raw.(string)
if !ok {
return fmt.Errorf("invalid %s value: expected one of none|unapproved|approved|merged", fieldName)
}
switch strings.ToLower(strings.TrimSpace(s)) {
case "none", "unapproved", "approved", "merged":
return nil
default:
return fmt.Errorf("invalid %s value: expected one of none|unapproved|approved|merged", fieldName)
}
var allowedIntegrityLevels = []string{"none", "unapproved", "approved", "merged"}
var allowedIntegrityLevelSet = map[string]struct{}{
"none": {},
"unapproved": {},
"approved": {},
"merged": {},
}
func invalidIntegrityFieldError(fieldName string) error {
return fmt.Errorf(
"invalid %s value: expected one of %s",
fieldName,
strings.Join(allowedIntegrityLevels, "|"),
)
}
// validateIntegrityField returns an error if raw is not a valid integrity-level
// string. fieldName is used in the error message (e.g. "disapproval-integrity").
func validateIntegrityField(fieldName string, raw interface{}) error {
s, ok := raw.(string)
if !ok {
return invalidIntegrityFieldError(fieldName)
}
normalized := strings.ToLower(strings.TrimSpace(s))
if _, ok := allowedIntegrityLevelSet[normalized]; ok {
return nil
}
return invalidIntegrityFieldError(fieldName)

Copilot uses AI. Check for mistakes.
@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 1, 2026

@copilot address the review feedback

…e in error messages

Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/e17d0df0-3b26-42a6-82ce-cd488a8c8b44

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot address the review feedback

Done in d1461dd. Two changes made:

  1. Centralised allowed integrity levelsallowedIntegrityLevels (slice) and allowedIntegrityLevelSet (map) are now the single source of truth in wasm_validate.go. A shared invalidIntegrityFieldError helper formats the error message, so the allowed-values string is written only once.

  2. Correct field name in error messageswasm_payload.go now tracks which key was actually present ("min-integrity" vs "integrity") and passes that name to validateIntegrityField, so errors reflect the field the caller actually supplied. Updated the corresponding test assertion to match.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build667973125/b509/launcher.test /tmp/go-build667973125/b509/launcher.test -test.testlogfile=/tmp/go-build667973125/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 973125/b160/_pkg-p mon/httpcommon.ggoogle.golang.org/grpc/balancer/endpointsharding x_amd64/compile (dns block)
    • Triggering command: /tmp/go-build996359952/b513/launcher.test /tmp/go-build996359952/b513/launcher.test -test.testlogfile=/tmp/go-build996359952/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -o rGai/HwxzEigtvtf4iKtLrGai -trimpath x_amd64/vet -p github.com/googl/tmp/go-build2959047884/b485/vet.cfg -lang=go1.16 x_amd64/vet -I pkg/mod/golang.org/x/net@v0.52.0/internal/httpsfv/httpsfv.go -I x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet (dns block)
  • invalid-host-that-does-not-exist-12345.com
    • Triggering command: /tmp/go-build667973125/b491/config.test /tmp/go-build667973125/b491/config.test -test.testlogfile=/tmp/go-build667973125/b491/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true -c=4 -nolocalimports -importcfg /tmp/go-build667973125/b386/importcfg -pack /home/REDACTED/go/pkg/mod/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp@v1.43.0/internal/otlpconfig/envconfig.go /home/REDACTED/go/pkg/mod/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp@v1.43.0/internal/otlpconfig/options.go 9731�� ache/go/1.25.9/x-p cfg x_amd64/compile (dns block)
    • Triggering command: /tmp/go-build996359952/b495/config.test /tmp/go-build996359952/b495/config.test -test.testlogfile=/tmp/go-build996359952/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s stlo�� olang.org/protobuf@v1.36.11/types/known/fieldmaskpb/field_mask.pb.go /tmp/go-build667973125/b154/vet.cfg cfg get g/protobuf/inter/tmp/go-build2959047884/b253/vet.cfg ut-1399233362.c ache/go/1.25.9/x64/pkg/tool/linux_amd64/compile -o 973125/b439/_pkg_.a -trimpath (dns block)
  • nonexistent.local
    • Triggering command: /tmp/go-build667973125/b509/launcher.test /tmp/go-build667973125/b509/launcher.test -test.testlogfile=/tmp/go-build667973125/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 973125/b160/_pkg-p mon/httpcommon.ggoogle.golang.org/grpc/balancer/endpointsharding x_amd64/compile (dns block)
    • Triggering command: /tmp/go-build996359952/b513/launcher.test /tmp/go-build996359952/b513/launcher.test -test.testlogfile=/tmp/go-build996359952/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -o rGai/HwxzEigtvtf4iKtLrGai -trimpath x_amd64/vet -p github.com/googl/tmp/go-build2959047884/b485/vet.cfg -lang=go1.16 x_amd64/vet -I pkg/mod/golang.org/x/net@v0.52.0/internal/httpsfv/httpsfv.go -I x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet (dns block)
  • slow.example.com
    • Triggering command: /tmp/go-build667973125/b509/launcher.test /tmp/go-build667973125/b509/launcher.test -test.testlogfile=/tmp/go-build667973125/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 973125/b160/_pkg-p mon/httpcommon.ggoogle.golang.org/grpc/balancer/endpointsharding x_amd64/compile (dns block)
    • Triggering command: /tmp/go-build996359952/b513/launcher.test /tmp/go-build996359952/b513/launcher.test -test.testlogfile=/tmp/go-build996359952/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -o rGai/HwxzEigtvtf4iKtLrGai -trimpath x_amd64/vet -p github.com/googl/tmp/go-build2959047884/b485/vet.cfg -lang=go1.16 x_amd64/vet -I pkg/mod/golang.org/x/net@v0.52.0/internal/httpsfv/httpsfv.go -I x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet (dns block)
  • this-host-does-not-exist-12345.com
    • Triggering command: /tmp/go-build667973125/b518/mcp.test /tmp/go-build667973125/b518/mcp.test -test.testlogfile=/tmp/go-build667973125/b518/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true ache/go/1.25.9/x-errorsas ache/go/1.25.9/x-ifaceassert 64/bin/as -p internal/profile-atomic -lang=go1.25 nk3xf9aLk6ss o_.o�� ce/internal/env/-errorsas -trimpath 64/pkg/tool/linu-nilfunc -p hash -lang=go1.25 64/pkg/tool/linu-buildtags (dns block)
    • Triggering command: /tmp/go-build996359952/b522/mcp.test /tmp/go-build996359952/b522/mcp.test -test.testlogfile=/tmp/go-build996359952/b522/testlog.txt -test.paniconexit0 -test.timeout=10m0s 9731�� /opt/hostedtoolcache/go/1.25.9/x64/src/os/user ache/go/1.25.9/x64/src/net/http/httputil/dump.go x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet -o /tmp/go-build667973125/b468/_pkg_.a -trimpath x_amd64/vet -p google.golang.or--version -lang=go1.24 x_amd64/vet (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of lpcox May 1, 2026 15:32
@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 1, 2026

@copilot address the review feedback

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot address the review feedback

Both review items were already addressed in d1461dd — the code is exactly as suggested. No further changes needed.

Copilot finished work on behalf of lpcox May 1, 2026 16:04
@lpcox lpcox merged commit 1a75624 into main May 1, 2026
17 checks passed
@lpcox lpcox deleted the copilot/duplicate-code-reduction-wasm-payload branch May 1, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code Pattern: Repeated Integrity-Level Switch Validation in wasm_payload.go

3 participants